package com.example.im.xmpp

import android.content.Context
import android.content.Context.VIBRATOR_SERVICE
import android.media.AudioAttributes
import android.net.ConnectivityManager
import android.os.Build
import android.os.VibrationEffect
import android.os.Vibrator
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.room.Room
import com.example.im.common.Enumerate
import com.example.im.common.AppDatabase
import com.example.im.common.ImApplication
import com.example.im.entity.*
import com.example.im.utils.JsonUtils
import com.example.im.utils.LogUtils
import com.example.im.utils.RandomUntil
import com.example.im.common.Constants
import org.greenrobot.eventbus.EventBus
import org.jivesoftware.smack.*
import org.jivesoftware.smack.chat2.Chat
import org.jivesoftware.smack.chat2.ChatManager
import org.jivesoftware.smack.chat2.IncomingChatMessageListener
import org.jivesoftware.smack.chat2.OutgoingChatMessageListener
import org.jivesoftware.smack.filter.AndFilter
import org.jivesoftware.smack.filter.StanzaTypeFilter
import org.jivesoftware.smack.packet.*
import org.jivesoftware.smack.roster.Roster
import org.jivesoftware.smack.roster.RosterEntry
import org.jivesoftware.smack.roster.RosterListener
import org.jivesoftware.smack.roster.SubscribeListener
import org.jivesoftware.smack.roster.packet.RosterPacket
import org.jivesoftware.smack.tcp.XMPPTCPConnection
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration
import org.jivesoftware.smackx.bookmarks.BookmarkManager
import org.jivesoftware.smackx.iqregister.AccountManager
import org.jivesoftware.smackx.muc.*
import org.jivesoftware.smackx.muc.packet.MUCOwner
import org.jivesoftware.smackx.muc.packet.MUCUser
import org.jivesoftware.smackx.muc.provider.MUCOwnerProvider
import org.jivesoftware.smackx.offline.OfflineMessageManager
import org.jivesoftware.smackx.ping.PingFailedListener
import org.jivesoftware.smackx.ping.PingManager
import org.jivesoftware.smackx.search.ReportedData
import org.jivesoftware.smackx.search.UserSearchManager
import org.jivesoftware.smackx.xdata.FormField
import org.jxmpp.jid.EntityBareJid
import org.jxmpp.jid.EntityFullJid
import org.jxmpp.jid.EntityJid
import org.jxmpp.jid.Jid
import org.jxmpp.jid.impl.JidCreate
import org.jxmpp.jid.parts.Domainpart
import org.jxmpp.jid.parts.Localpart
import org.jxmpp.jid.parts.Resourcepart
import java.lang.Exception

import java.net.InetAddress
import java.security.KeyStore
import java.util.*

class IMConnection : ConnectionListener, StanzaListener, SubscribeListener, RosterListener,
    PingFailedListener, OutgoingChatMessageListener, IncomingChatMessageListener,
    InvitationListener, MessageListener {

    private var mConnection: AbstractXMPPConnection? = null

    private var mChatManager: ChatManager? = null

    private var mPingManager: PingManager? = null

    private var mMucManager: MultiUserChatManager? = null

    private var mRoster: Roster? = null

    private var pingTimer: Timer? = null

    private var mucMaps = mutableMapOf<String, MultiUserChat>()

    private var isInitialization: Boolean = false

    init {
//        SmackConfiguration.DEBUG = true
        val config: XMPPTCPConnectionConfiguration = XMPPTCPConnectionConfiguration.builder()
            .setXmppDomain(XMPP_SERVICE_DONAIN)
            .setHostAddress(InetAddress.getByName(XMPP_SERVICE_HOST))
            .setPort(XMPP_SERVICE_PORT)
            .setSendPresence(false)
            .setResource("Phone")
            .setCompressionEnabled(true)
            .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
            .build()
        mConnection = XMPPTCPConnection(config)
        //连接监听
        mConnection!!.addConnectionListener(this)
        //IQ包监听
        val filter = AndFilter(StanzaTypeFilter(Presence::class.java))
        mConnection!!.addSyncStanzaListener(this, filter)
        //设置经过同意才可以添加好友
        Roster.setDefaultSubscriptionMode(Roster.SubscriptionMode.manual)
    }


    /**
     * 连接监听
     * */
    override fun connected(connection: XMPPConnection?) {
        LogUtils.e("连接成功")
    }

    override fun connectionClosed() {
        LogUtils.e("关闭连接")
        isInitialization = false

        if (mucMaps.isNotEmpty()) {
            mucMaps.clear()
        }
        //取消定时器
        if (pingTimer != null) {
            pingTimer!!.cancel()
        }
        //移除花名册监听
        if (mRoster != null) {
            mRoster!!.removeRosterListener(this)
            mRoster!!.removeSubscribeListener(this)
        }
        //移除聊天室邀请监听
        if (mMucManager != null) {
            mMucManager!!.removeInvitationListener(this)
        }
        //移除收发消息监听
        if (mChatManager != null) {
            mChatManager!!.removeIncomingListener(this)
            mChatManager!!.removeOutgoingListener(this)
        }
        //注销PING
        if (mPingManager != null) {
            mPingManager!!.unregisterPingFailedListener(this)
        }
    }

    override fun connectionClosedOnError(e: Exception?) {
        LogUtils.e("连接异常：$e")

        isInitialization = false
        if (e is XMPPException.StreamErrorException) {
            //登录冲突
            if (e.streamError.toString()
                    .contains("urn:ietf:params:xml:ns:xmpp-streams")
            ) {
                mConnection!!.disconnect()
                mConnection = null
                imConnection = null
                val supporter = EventBusData<Any>(
                    Enumerate.Handle.base_resource
                )
                EventBus.getDefault().post(supporter)
            }
        }
    }

    override fun authenticated(connection: XMPPConnection?, resumed: Boolean) {
        LogUtils.e("认证成功")
        //获取登录者信息
        val mAccount = AccountManager.getInstance(connection)
        //创建数据库（不同用户创建不同的数据库）
        val dbName = "${mAccount.getAccountAttribute("username")}.db"
        ImApplication.appDatabase = Room.databaseBuilder(
            ImApplication.context(), AppDatabase::class.java, dbName
        )
            .allowMainThreadQueries().build()
        //保存登录者信息
        setAccountStorage(mAccount)


        //初始化花名册获取并监听
        mRoster = Roster.getInstanceFor(connection)
        mRoster!!.entries.forEach {
            setFriendOrRoomStorage(it)
        }
        mRoster!!.addSubscribeListener(this)
        mRoster!!.addRosterListener(this)

        //初始化收发消息监听
        mChatManager = ChatManager.getInstanceFor(connection)
        mChatManager!!.addOutgoingListener(this)
        mChatManager!!.addIncomingListener(this)

        //初始化聊天室邀请监听
        mMucManager = MultiUserChatManager.getInstanceFor(connection)
        mMucManager!!.addInvitationListener(this)

        //获取书签房间
        val bookmark = BookmarkManager.getBookmarkManager(connection)
        bookmark.bookmarkedConferences?.forEach {
            roomBookOrJoin(
                mMucManager!!.getMultiUserChat(it.jid), mMucManager!!.getRoomInfo(it.jid), false
            )
        }

        //初始化心跳包监听
        mPingManager = PingManager.getInstanceFor(connection)
        mPingManager!!.registerPingFailedListener(this)
        pingTimer = Timer()
        pingTimer!!.schedule(PingMyServerTimerTask(mPingManager!!), TIME_DELAY, 1000 * 3)

        //设置在线
        val presence = Presence(Presence.Type.available)
        presence.mode = Presence.Mode.chat
        connection!!.sendStanza(presence)

        isInitialization = true


//        //获取离线消息
//        val offManager = OfflineMessageManager(connection)
//        offManager.messages.forEach {
//            LogUtils.e("离线消息：from：${it.from.asBareJid()}，message：${it.body}")
//            if (it.from.domain.contains("conference")) {
//                //加入聊天室
//                val manager = MultiUserChatManager.getInstanceFor(connection)
//                val muc = manager.getMultiUserChat(it.from.asEntityBareJidIfPossible())
//                roomBookOrJoin(muc!!, manager.getRoomInfo(muc.room), true)
//            } else {
//                if (it.body != null) setMessageStorage(
//                    it.from.localpartOrNull.intern(),
//                    it, Constants.CHAT_RECEIVE
//                )
//            }
//        }
//        offManager.deleteMessages()
    }

    /**
     * 用户状态监听
     * */
    override fun processStanza(packet: Stanza?) {
        val from: String = packet!!.from.asBareJid().toString()
        val to: String = packet.to.asBareJid().toString()
        val sb = StringBuilder()
        if (packet is Presence) when (packet.type!!) {
            Presence.Type.subscribe -> sb.append("请求订阅")
            Presence.Type.subscribed -> sb.append("同意订阅")
            Presence.Type.unsubscribe -> {
                sb.append("取消订阅")
                val entry = mRoster!!.getEntry(packet.from!!.asBareJid())
                if (entry.type == RosterPacket.ItemType.none) {
                    setFriendOrRoomStorage(entry)
                }
            }
            Presence.Type.unsubscribed -> {
                sb.append("拒绝订阅")
                val entry = mRoster!!.getEntry(packet.from!!.asBareJid())
                if (entry.type == RosterPacket.ItemType.none) {
                    setFriendOrRoomStorage(entry)
                }
            }
            Presence.Type.unavailable -> {
                sb.append("离线")
                if (from == to) {
                    //设置在线
                    val presence = Presence(Presence.Type.available)
                    presence.mode = Presence.Mode.chat
                    mConnection!!.sendStanza(presence)
                }
            }
            Presence.Type.available -> sb.append("上线")
            Presence.Type.error -> sb.append("error")
            Presence.Type.probe -> sb.append("probe")
        }
        LogUtils.e(sb.append("，from：").append(from).append("，to：").append(to).toString())
    }


    override fun processSubscribe(
        from: Jid?,
        subscribeRequest: Presence?
    ): SubscribeListener.SubscribeAnswer {
        return SubscribeListener.SubscribeAnswer.ApproveAndAlsoRequestIfRequired
    }

    override fun entriesAdded(addresses: MutableCollection<Jid>?) {

        addresses!!.forEach {
            val entry = mRoster!!.getEntry(it.asBareJid())
            if (entry.type != RosterPacket.ItemType.from)
                return
            val user = it.localpartOrNull.intern()
                ?: return
            entry.name = userSearch(user)[0]
                .getValues("Name")[0].toString()
            setFriendOrRoomStorage(entry)
        }
    }

    override fun entriesUpdated(addresses: MutableCollection<Jid>?) {

        addresses!!.forEach {
            val entry = mRoster!!.getEntry(it.asBareJid())
            if (entry.type != RosterPacket.ItemType.both)
                return
            setFriendOrRoomStorage(entry)
        }
    }

    override fun entriesDeleted(addresses: MutableCollection<Jid>?) {

    }

    override fun presenceChanged(presence: Presence?) {

    }

    /**
     * 服务器ping失败时调用
     * */
    override fun pingFailed() {
        LogUtils.e("服务器PING客户端失败,正在断开重连...")
        mConnection!!.disconnect()
        reconnect()
    }

    /**
     * 发送消息监听
     * */
    override fun newOutgoingMessage(to: EntityBareJid?, message: Message?, chat: Chat?) {
        LogUtils.e("发送消息：to：${to!!.asBareJid()}，message：${message}")
        message!!.from = mConnection!!.user
        setMessageStorage(
            to.localpartOrNull.intern(),
            message,
            Constants.CHAT_SEND,
            Constants.CHAT_STATUS_0
        )
    }

    /**
     * 接收消息监听
     * */
    @RequiresApi(Build.VERSION_CODES.O)
    override fun newIncomingMessage(from: EntityBareJid?, message: Message?, chat: Chat?) {
        LogUtils.e("接收消息：from：${from!!.asBareJid()}，message：${message!!.body}")
        setMessageStorage(
            from.localpartOrNull.intern(),
            message,
            Constants.CHAT_RECEIVE,
            Constants.CHAT_STATUS_1
        )
        val vibrator = ImApplication.context().getSystemService(VIBRATOR_SERVICE) as Vibrator
        val aab = AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_MOVIE)
            .build()
        vibrator.vibrate(VibrationEffect.createOneShot(200, 200)!!, aab)
    }

    /**
     * 群聊消息监听
     * */
    override fun processMessage(message: Message?) {
        LogUtils.e("群聊消息：from：${message!!.from!!}，message：${JsonUtils.objectToString(message.extensions)}")
        if (message.body != null) {
            val or = if (message.from.resourceOrEmpty.contains(message.to.localpartOrNull))
                Constants.CHAT_SEND else Constants.CHAT_RECEIVE
            val status = if (message.from.resourceOrEmpty.contains(message.to.localpartOrNull))
                Constants.CHAT_STATUS_0 else Constants.CHAT_STATUS_1
            setMessageStorage(
                message.from!!.localpartOrNull.intern(), message, or, status
            )
        }
    }

    /**
     * 聊天室邀请监听
     * */
    override fun invitationReceived(
        conn: XMPPConnection?,
        muc: MultiUserChat?,
        inviter: EntityJid?,
        reason: String?,
        password: String?,
        message: Message?,
        invitation: MUCUser.Invite?
    ) {
        LogUtils.e("邀请加群：")
        val manager = MultiUserChatManager.getInstanceFor(conn)
        roomBookOrJoin(muc!!, manager.getRoomInfo(muc.room), true)
    }


    /**
     * 判断连接是否成功
     *
     * @return true/false
     */
    private val isConnected: Boolean
        get() = mConnection != null && mConnection!!.isConnected

    /**
     * 判断连接是否通过了身份验证
     *
     * @return true/false
     */
    val isAuthenticated: Boolean
        get() = mConnection != null && mConnection!!.isConnected && mConnection!!.isAuthenticated

    /**
     * 重新连接重连
     * */
    fun reconnect() {
        if (!isAuthenticated) mConnection!!.connect().login()
    }

    /**
     * 用户登录
     *
     * @return true/false
     */
    fun login(userName: String, userPwd: String): Boolean {
        if (isConnected)
            mConnection!!.disconnect()
        mConnection!!.connect().login(userName, userPwd)
        return mConnection!!.isAuthenticated
    }

    /**
     * 用户注册
     *
     * @return true/false
     */
    fun logon(userName: String, name: String, userPwd: String): Boolean {
        if (isConnected)
            mConnection!!.disconnect()
        val account: AccountManager = AccountManager.getInstance(mConnection!!.connect())
        account.sensitiveOperationOverInsecureConnection((true))
        account.accountAttributes
        account.createAccount(
            Localpart.from(userName), userPwd, mutableMapOf(
                "name" to name
            )
        )
        mConnection!!.disconnect()
        mConnection = null
        imConnection = null
        return true
    }

    /**
     * 切换用户
     * */
    fun exit() {
        //设置离线
        val presence = Presence(Presence.Type.unavailable)
        mConnection!!.sendStanza(presence)
        //断开连接
        mConnection!!.disconnect()
        mConnection = null
        imConnection = null
    }

    /**
     * 修改密码
     * @param newPwd    新密码
     * @return
     */
    fun changePassword(newPwd: String): Boolean {
        val account = AccountManager.getInstance(mConnection)
        account.changePassword(newPwd)
        return true
    }

    /**
     * 搜索用户
     *
     * @param search 搜索内容
     */
    fun userSearch(search: String): List<ReportedData.Row> {
        if (!isAuthenticated)
            return arrayListOf()
        val manager = UserSearchManager(mConnection)
        val domain = JidCreate.domainBareFrom("search.$XMPP_SERVICE_DONAIN")
        val from = with(manager.getSearchForm(domain).createAnswerForm()) {
            setAnswer("Username", true)
            setAnswer("search", search)
            this
        }
        return manager.getSearchResults(from, domain).rows
    }

    /**
     * 添加好友
     *
     * @param userName 用户jid
     * @param name     命名
     */
    fun createEntry(userName: String, name: String): Boolean {
        val bareJid = JidCreate.entityBareFrom(
            Localpart.from(userName),
            Domainpart.from(XMPP_SERVICE_DONAIN)
        )
        val roster = Roster.getInstanceFor(mConnection)
        roster.createEntry(bareJid, name, null)
        return true
    }

    /**
     * 删除好友
     * */
    fun removeEntry(jid: String): Boolean {
        val roster = Roster.getInstanceFor(mConnection)
        val entry = roster.getEntry(JidCreate.bareFrom(jid))
        if (entry != null)
            roster.removeEntry(entry)
        return true
    }


    /**
     * 发送消息
     *
     * @param name       接收者用户名
     * @param msg        发送数据
     */
    fun sendChatMessage(name: String, msg: String, type: Message.Type): Any {
        if (!isAuthenticated && !isInitialization) {
            return "连接被断开，无法发送消息"
        }
        val bareJid = when (type) {
            Message.Type.chat -> JidCreate.entityBareFrom("$name@$XMPP_SERVICE_DONAIN")
            Message.Type.groupchat -> JidCreate.entityBareFrom("$name@conference.$XMPP_SERVICE_DONAIN")
            else -> null
        }
            ?: return false
        if (type == Message.Type.chat && !mRoster!!.isSubscribedToMyPresence(bareJid)) {
            return "你已被对方移除好友列表，无法发送消息"
        }
        if (type == Message.Type.groupchat && !isRoomOwners(name)) {
            return "你已被管理员移除该群聊，无法发送消息"
        }
        val message = with(Message()) {
            this.body = msg
            this.type = type
            this
        }
        if (type == Message.Type.chat) {
            val manager = ChatManager.getInstanceFor(mConnection)
            manager.chatWith(bareJid).send(message)
        } else if (type == Message.Type.groupchat) {
            val manager = MultiUserChatManager.getInstanceFor(mConnection)
            val muc = manager.getMultiUserChat(bareJid)
            muc.sendMessage(message)
        }
        return true
    }

    private fun isRoomOwners(name: String): Boolean {
        val muc = mucMaps[name]
            ?: return false
        val user = mConnection!!.user
            ?: return false
        return !muc.owners.none {
            it.jid.localpartOrThrow.intern() == user.localpart.intern()
        }
    }


    /**
     * 创建新房间
     * */
    fun roomCreate(naturalName: String): FriendOrRoom {

        if (!isAuthenticated) {
            return FriendOrRoom()
        }

        val roomJid = RandomUntil.getNumLargeSmallLetter(10)
        val conference = "$roomJid@conference.$XMPP_SERVICE_DONAIN"
        val bareJid = JidCreate.entityBareFrom(conference)
        val account = ImApplication.appDatabase()
            .accountDao().account()
        val nickName = "${account.name}(${account.username})"

        val manager = MultiUserChatManager.getInstanceFor(mConnection)
        val muc = manager.getMultiUserChat(bareJid)
        muc.create(Resourcepart.from(nickName))
        val form = muc.configurationForm
        LogUtils.e(JsonUtils.objectToString(form))
        val submitForm = form.createAnswerForm()
        form.fields.forEach {
            if (it.variable != null && it.type != FormField.Type.hidden) {
                submitForm.setDefaultAnswer(it.variable)
            }
        }
        submitForm.setAnswer("muc#roomconfig_roomname", naturalName)
        submitForm.setAnswer("muc#roomconfig_persistentroom", true)
        submitForm.setAnswer("muc#roomconfig_membersonly", false)
        submitForm.setAnswer("muc#roomconfig_allowinvites", true)
        submitForm.setAnswer("muc#roomconfig_passwordprotectedroom", false)
        submitForm.setAnswer("muc#roomconfig_whois", arrayListOf("anyone"))
        submitForm.setAnswer("muc#roomconfig_enablelogging", true)
        submitForm.setAnswer("x-muc#roomconfig_reservednick", true)
        submitForm.setAnswer("x-muc#roomconfig_canchangenick", true)
        submitForm.setAnswer("x-muc#roomconfig_registration", true)
        muc.sendConfigurationForm(submitForm)

        return roomBookOrJoin(muc, manager.getRoomInfo(bareJid), true)
    }

    /**
     * 邀请好友
     * */
    fun roomInvite(roomName: String, members: List<String>): Boolean {
        if (!isAuthenticated)
            return false
        val muc = mucMaps[roomName]
            ?: return false
        members.forEach {
            muc.grantOwnership(JidCreate.from(it))
            muc.invite(JidCreate.entityBareFrom(it), "邀请你加入群聊")
        }
        return true
    }

    /**
     * 加入书签并加入房间
     * */
    private fun roomBookOrJoin(
        muc: MultiUserChat,
        roomInfo: RoomInfo,
        isBookmark: Boolean
    ): FriendOrRoom {

        val account = ImApplication.appDatabase()
            .accountDao().account()
        val nickName = "${account.name}(${account.username})"

        //加入书签
        if (isBookmark) BookmarkManager.getBookmarkManager(mConnection).addBookmarkedConference(
            muc.room.localpart.intern(), muc.room, false, Resourcepart.from(nickName), null
        )
        //加入房间
        val builder = muc.getEnterConfigurationBuilder(Resourcepart.from(nickName))

        val last = ImApplication.appDatabase().chatRecordLastDao()
            .getChatRecordLast(
                muc.room.localpart.intern(),
                Message.Type.groupchat.ordinal
            )
        if (last != null) {
            val entireLast = ImApplication.appDatabase().chatRecordEntireDao()
                .selectChatRecordEntireLastTime(last.id)
            if (entireLast != null) {
                builder.requestHistorySince(Date(entireLast.time + 1000))
            } else {
                builder.requestHistorySince(0)
            }
        } else {
            builder.requestHistorySince(0)
        }

        muc.join(builder.build())

        muc.addUserStatusListener(object : DefaultUserStatusListener() {
            override fun ownershipRevoked() {
                super.ownershipRevoked()
                removeMessageListener(muc.room.localpart.intern())
                setFriendOrRoomStorage(roomInfo, 0)
            }
        })

        val isMUCOwner = muc.owners.none {
            it.jid.localpartOrNull.intern() == mConnection!!.user.localpart.intern()
        }
        if (!isMUCOwner) muc.addMessageListener(this)

        mucMaps[muc.room.localpart.intern()] = muc

        return setFriendOrRoomStorage(roomInfo, if (!isMUCOwner) 3 else 0)
    }

    fun removeMessageListener(roomName: String) {
        mucMaps[roomName]!!.removeMessageListener(this)
    }

    /**
     *获取群拥有者
     * */
    fun roomOwners(roomName: String): MutableList<Affiliate> {
        if (!isAuthenticated)
            return mutableListOf()
        val muc = mucMaps[roomName]
            ?: return mutableListOf()
        val affiliate = mutableListOf<Affiliate>()
        affiliate.addAll(muc.owners)
        return affiliate
    }


    /**
     * 主动退出群聊
     * */
    fun roomLeave(roomName: String, roomJid: String): Boolean {
        if (!isAuthenticated)
            return false
        val muc = mucMaps[roomName]!!
        val owners = muc.owners
        if (owners.none {
                it.jid.localpartOrNull.intern() == mConnection!!.user.localpart.intern()
            }) {
            muc.leave()
        } else {
            if (owners.size == 1 && owners[0].jid.localpartOrNull.intern() == mConnection!!.user.localpart.intern()) {
                muc.destroy("", JidCreate.entityBareFrom(roomJid))
            } else {
                muc.revokeOwnership(mConnection!!.user)
                muc.revokeMembership(mConnection!!.user)
            }
        }
        muc.removeMessageListener(this)
        mucMaps.remove(roomName)
        BookmarkManager.getBookmarkManager(mConnection)
            .removeBookmarkedConference(JidCreate.entityBareFrom(roomJid))
        return true
    }

    /**
     * 踢除人员
     * */
    fun roomKick(roomName: String, userJid: String): Boolean {
        if (!isAuthenticated)
            return false
        val muc = mucMaps[roomName]!!
        muc.revokeOwnership(JidCreate.from(userJid))
        muc.revokeMembership(JidCreate.from(userJid))
        return true
    }

    /**
     * 保存账户信息
     * */
    private fun setAccountStorage(mAccount: AccountManager) {
        val account = with(AccountInfo(), {
            this.name = mAccount.getAccountAttribute("name")
            this.email = mAccount.getAccountAttribute("email")
            this.username = mAccount.getAccountAttribute("username")
            this.getAccount()
        })
        ImApplication.appDatabase().accountDao().insert(account)
        LogUtils.e("==========setAccountStorage==========${JsonUtils.objectToString(ImApplication.appDatabase().accountDao().account())}")
    }


    private fun setFriendOrRoomStorage(friendOrRoom: FriendOrRoom): FriendOrRoom {

        LogUtils.e("=用户==${JsonUtils.objectToString(friendOrRoom)}")
        val mFriendOrRoomDB = ImApplication.appDatabase().friendOrRoomDao()
            .selectFriendOrRoomByName(friendOrRoom.account)
        if (mFriendOrRoomDB != null) {
            //更新数据库
            val friendOrRoomNew = with(friendOrRoom) {
                this.id = mFriendOrRoomDB.id
                this.getFriendOrRoom()
            }
            ImApplication.appDatabase().friendOrRoomDao()
                .update(friendOrRoomNew)
            return friendOrRoomNew
        }
        //写入数据库
        val id = ImApplication.appDatabase().friendOrRoomDao()
            .insert(friendOrRoom)

        //获取最新数据
        val mFriendOrRoomNew = ImApplication.appDatabase().friendOrRoomDao()
            .selectFriendOrRoomById(id.toInt())

        //通知页面更新
        val handle = if (mFriendOrRoomNew.type == Message.Type.chat.ordinal)
            Enumerate.Handle.list_friend else Enumerate.Handle.list_group
        val action = Enumerate.Action.list_add
        val busData = EventBusData<Any>(handle, action, mFriendOrRoomNew)
        EventBus.getDefault().post(busData)

        return mFriendOrRoomNew
    }

    private fun setFriendOrRoomStorage(roster: RosterEntry) {
        val friendOrRoom = with(FriendOrRoom(), {
            this.jid = roster.jid.intern()
            this.account = roster.jid.localpartOrNull.intern()
            this.nickname = roster.name
            this.type = Message.Type.chat.ordinal
            this.sub = roster.type.ordinal
            this.getFriendOrRoom()
        })
        this.setFriendOrRoomStorage(friendOrRoom)
    }

    private fun setFriendOrRoomStorage(room: RoomInfo, sub: Int): FriendOrRoom {
        val friendOrRoom = with(FriendOrRoom(), {
            this.jid = room.room.intern()
            this.account = room.room.localpart.intern()
            this.nickname = room.name
            this.type = Message.Type.groupchat.ordinal
            this.sub = sub
            this.getFriendOrRoom()
        })
        return this.setFriendOrRoomStorage(friendOrRoom)
    }

    /**
     * 保存聊天记录
     * */
    fun setMessageStorage(
        userName: String, msg: Message, or: Int, status: Int
    ): ChatRecordEntire {

        val fId = ImApplication.appDatabase().friendOrRoomDao()
            .selectFriendOrRoomByName(userName)?.id ?: 0

        val from = if (msg.from != null) msg.from.intern() else null
        val to = if (msg.to != null) msg.to.intern() else null
        val type = msg.type.ordinal
        val time = System.currentTimeMillis()
        val content = try {
            JsonUtils.stringToObject(msg.body, ChatContent::class.java)
        } catch (e: Exception) {
            with(ChatContent(), {
                this.body = msg.body
                this.type = Constants.CONTENT_TYPE_TEXT
                this
            })
        }

        if (or == Constants.CHAT_RECEIVE) {
            content.entireId = 0
            if (content.fileInfo != null) {
                content.fileInfo!!.localUrl = null
            }
        }
        val body = JsonUtils.objectToString(content)

        val recordLast1 = ImApplication.appDatabase().chatRecordLastDao()
            .getChatRecordLastByFIdAndType(fId, type)
        val recordLast2 = with(recordLast1 ?: ChatRecordLast(), {
            this.fId = fId
            this.body = body
            this.name = if (msg.type == Message.Type.groupchat) {
                if (or == Constants.CHAT_RECEIVE) {
                    from!!.substring(from.indexOf("/") + 1, from.length)
                } else null
            } else null
            this.time = time
            this.type = type
            this
        })

        val lastId = if (recordLast1 == null) {
            ImApplication.appDatabase().chatRecordLastDao()
                .insert(recordLast2).toInt()
        } else {
            ImApplication.appDatabase().chatRecordLastDao()
                .update(recordLast2)
            recordLast2.id
        }

        val recordEntire1 = ImApplication.appDatabase!!.chatRecordEntireDao()
            .getChatRecordEntireByID(content.entireId)

        val recordEntire2 = with(recordEntire1 ?: ChatRecordEntire(), {
            this.lastId = lastId
            this.from = from
            this.to = to
            this.body = body
            this.itel = if (or == Constants.CHAT_RECEIVE)
                content.itel else null
            this.time = time
            this.or = or
            this.status = status
            this
        })
        val entireId = if (recordEntire1 == null) {
            ImApplication.appDatabase().chatRecordEntireDao()
                .insert(recordEntire2).toInt()
        } else {
            ImApplication.appDatabase().chatRecordEntireDao()
                .update(recordEntire2)
            recordEntire2.id
        }

        val last = ImApplication.appDatabase().chatRecordLastDao()
            .getChatRecordLastByID(lastId)
        val entire = ImApplication.appDatabase().chatRecordEntireDao()
            .getChatRecordEntireByID(entireId)

        LogUtils.e(JsonUtils.objectToString(last))
        LogUtils.e(JsonUtils.objectToString(entire))

        //通知刷新聊天列表页面
        val data = EventBusData<MutableMap.MutableEntry<ChatRecordLast, ChatRecordEntire>>(
            Enumerate.Handle.list_chat, Enumerate.Action.list_add,
            AbstractMap.SimpleEntry<ChatRecordLast, ChatRecordEntire>(last, entire)
        )
        EventBus.getDefault().post(data)

        return entire
    }


    inner class PingMyServerTimerTask(
        private val pingManager: PingManager
    ) : TimerTask() {

        override fun run() {

            val log = "客户端向服务端发送心跳包："
            if (!isNetworkState()) {
                LogUtils.e("${log}网络连接不可用")
                return
            }
            if (!isAuthenticated) {
                LogUtils.e("${log}重新连接")
                mConnection!!.disconnect()
                reconnect()
                return
            }
            if (pingManager.pingMyServer(false, TIME_DELAY)) {
                LogUtils.e("${log}PING成功")
            } else {
                LogUtils.e("${log}PING失败")
                mConnection!!.disconnect()
                reconnect()
//                var i = 0
//                while (i < 2) {
//                    if (!isNetworkState()) {
//                        LogUtils.e("${log}重新尝试${i + 1}网络连接不可用")
//                        break
//                    }
//                    if (!isAuthenticated) {
//                        LogUtils.e("${log}重新尝试${i + 1}重新连接")
//                        mConnection!!.disconnect()
//                        reconnect()
//                        break
//                    }
//                    if (pingManager.pingMyServer(i == 1, TIME_DELAY)) {
//                        LogUtils.e("${log}重新尝试${i + 1}PING成功")
//                        break
//                    }
//                    LogUtils.e("${log}重新尝试${i + 1}PING失败")
//
//                    i++
//                }

            }
        }

        private fun isNetworkState(): Boolean {
            val connManager =
                ImApplication.context().getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

            val networkInfo = connManager.activeNetworkInfo

            return networkInfo != null && networkInfo.isConnected
        }

    }

    companion object {

        private val XMPP_SERVICE_DONAIN = "openfire-voc"
        private val XMPP_SERVICE_HOST = Constants.SERVICE_IP
        private val XMPP_SERVICE_PORT = 5222

        private val TIME_DELAY = 1000L

        @Volatile
        private var imConnection: IMConnection? = null

        val instance: IMConnection?
            get() {
                if (imConnection == null) synchronized(IMConnection::class.java) {
                    if (imConnection == null)
                        imConnection = IMConnection()
                }
                return imConnection
            }
    }

}
